package main

import (
	"encoding/json"
	"fmt"
	"github.com/fatih/color"
	"github.com/hpcloud/tail"
	"github.com/olekukonko/tablewriter"
	"github.com/urfave/cli/v2"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"pm/process"
	"regexp"
	"strconv"
	"strings"
	"time"
)

const URI = "http://127.0.0.1:17456/"




type httpRes struct {
	Status int `json:"status"`
	Msg string `json:"msg"`
	Data interface{} `json:"data"`
}


func httpPost(method string,params map[string]interface{})( httpRes,  error) {
	var arg = ""
	for k, v := range params {
		if len(arg) != 0 {
			arg += "&"
		}
		arg += fmt.Sprintf("%s=%s", k, v)
	}
	resp, err := http.Post(URI + method, "application/x-www-form-urlencoded", strings.NewReader(arg))
	if err != nil{
		return httpRes{}, nil
	}
	defer resp.Body.Close()
	data, err1 := ioutil.ReadAll(resp.Body)
	if err1 != nil {
		return httpRes{}, err
	}
	var res httpRes
	json.Unmarshal(data, &res)
	return res, nil
}


func runMainProgress( state chan int) {
	cmd := exec.Command("/usr/local/pm/bin/pm-admin"+process.Suffix)
	cmd.Env = os.Environ()
	cmd.Stdin = os.Stdin
	//cmd.Stderr = ioutil.Discard
	//cmd.Stdout = ioutil.Discard
	err := cmd.Start()
	if err != nil{
		fmt.Println(err.Error())
	}

	err = cmd.Wait()
	if err != nil{
		state <- 1
	}
}

func main() {
	respmain, err1 := http.Get(URI +"init")
	if err1 != nil {
		 var state chan int
		go runMainProgress(state)
		 ticker := time.NewTicker(3 * time.Second)
		select {
		case <-state:
			fmt.Println("服务端启动失败")
			ticker.Stop()
			os.Exit(0)
		case <-ticker.C:
			ticker.Stop()

		 }
	}else{
		respmain.Body.Close()
	}
	app := &cli.App{
		Commands: []*cli.Command{
			{
				Name:    "run",
				Aliases: []string{"a"},
				Usage:   "add a task to the list",
				Action: func(c *cli.Context) error {
					arr := c.Args().Slice()
					if len(arr) < 1 {
						fmt.Println("缺少参数")
					} else {
						params := map[string]interface{}{"name":arr[0]}
						if len(arr) > 1{
							params["arg"] = strings.Join(arr[1:], "")
						}
						if ! path.IsAbs(arr[0]){
							arr[0],_ = filepath.Abs(arr[0])
						}
						res, err := httpPost("run", map[string]interface{}{"name": arr[0], "arg":strings.Join(arr[1:], " ")})
						if err != nil {
							fmt.Println("请求失败:", err.Error())
						} else {
							if res.Status != 0{
								fmt.Println("启动失败:", res.Msg)
							}else{
								showList()
							}
						}
					}
					return nil
				},
			},
			{
				Name:    "info",
				Aliases: []string{"a"},
				Usage:   "show info",
				Action: func(c *cli.Context) error {
					arr := c.Args().Slice()
					if len(arr) < 1{
						fmt.Println("缺少参数")
						return nil
					}
					showInfo(arr[0])
					return nil
				},
			},
			{
				Name:    "reload",
				Aliases: []string{"r"},
				Usage:   "reload pm-admin",
				Action: func(c *cli.Context) error {
					res, err := httpPost("sys/reload", map[string]interface{}{})
					if err != nil {
						fmt.Println("请求失败:", err.Error())
					} else {
						if res.Status != 0 {
							fmt.Println("操作失败:", res.Msg)
						}
					}
						return nil
				},
			},
			{
				Name:    "restart",
				Aliases: []string{"rs"},
				Usage:   "restart pm-admin",
				Action: func(c *cli.Context) error {
					_, err := httpPost("sys/stop", map[string]interface{}{})
					if err != nil {
						fmt.Println("请求失败:", err.Error())
					}
					var state chan int
					go runMainProgress(state)
					ticker := time.NewTicker(3 * time.Second)
					select {
					case <-state:
						fmt.Println("服务端启动失败")
						ticker.Stop()
						os.Exit(0)
					case <-ticker.C:
						ticker.Stop()

					}
					return nil
				},
			},
			{
				Name:    "start",
				Aliases: []string{"s"},
				Usage:   "start progress",
				Action: func(c *cli.Context) error {
					arr := c.Args().Slice()
					if len(arr) < 1{
						fmt.Println("缺少参数")
						return nil
					}
					var mp = make(map[string]interface{})
					mp["key"] = arr[0]
					res, err := httpPost("start", mp)
					if err != nil {
						fmt.Println("请求失败:", err.Error())
					} else {
						if res.Status != 0{
							fmt.Println("操作失败:", res.Msg)
						}else{
							showList()
						}
					}
					return nil
				},
			},
			{
				Name:    "delete",
				Aliases: []string{"a"},
				Usage:   "delete idx from list",
				Action: func(c *cli.Context) error {
					arr := c.Args().Slice()
					if len(arr) < 1 {
						fmt.Println("缺少参数")
					} else {
						var mp = make(map[string]interface{})
						mp["key"] = arr[0]
						res, err := httpPost("delete", mp)
						if err != nil {
							fmt.Println("请求失败:", err.Error())
						} else {
							if res.Status != 0{
								fmt.Println("操作失败:", res.Msg)
							}else{
								showList()
							}
						}
					}
					return nil
				},
			},
			{
				Name:    "stop",
				Aliases: []string{"s"},
				Usage:   "stop progress",
				Action: func(c *cli.Context) error {
					arr := c.Args().Slice()
					if len(arr) < 1 {
						fmt.Println("缺少参数")
					} else {
						var mp = make(map[string]interface{})
						mp["key"] = arr[0]
						res, err := httpPost("stop", mp)
						if err != nil {
							fmt.Println("请求失败:", err.Error())
						} else {
							if res.Status != 0{
								fmt.Println("操作失败:", res.Msg)
							}else{
								showList()
							}
						}
					}
					return nil
				},
			},
			{
				Name:    "list",
				Aliases: []string{"l"},
				Usage:   "stop progress",
				Action: func(c *cli.Context) error {
					showList()
					return nil
				},
			},
			{
				Name:    "log",
				Aliases: []string{"l"},
				Usage:   "show log from idx",
				Action: func(c *cli.Context) error {
					arr := c.Args().Slice()
					if len(arr) < 1 {
						fmt.Println("缺少参数")
					}
						mp := make(map[string]interface{})
						if len(arr) > 0{
							mp["key"] = arr[0]
						}
						res, err := httpPost("info", mp)
						if err != nil {
							fmt.Println("请求失败:", err.Error())
						} else {
							 errorLog := false
							if len(arr) > 1 && arr[1] == "e" {
								errorLog = true
							}
							if res.Data == nil{
								return nil
							}
							pInfo := res.Data.(map[string]interface {})
							logFile := pInfo["log"].(string)
							if errorLog{
								logFile =  pInfo["error_log"].(string)
							}
							var fileSize int64
							file, err2:= os.Open(logFile)
							if err2 == nil{
								fileInfo,_ := file.Stat()
								fileSize = fileInfo.Size()
							}
							var offset int64 = -1024
							if fileSize < 1024{
								offset = 1024
							}
							seek := &tail.SeekInfo{Offset:offset,Whence:os.SEEK_END}
							tailFile, _ := tail.TailFile(logFile, tail.Config{
								Location:seek,
								ReOpen:    true,
								Follow:    true,
								MustExist: false,
								Poll:      true,
							})
							for {
								msg, ok := <-tailFile.Lines
								if !ok {
									continue
								}
								if len(msg.Text) < 19{
									fmt.Println(msg.Text)
									continue
								}
								timePre := msg.Text[0:19]
								if !regexp.MustCompile(`\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}`).MatchString(timePre){
									fmt.Println(msg.Text)
									continue
								}
								data := msg.Text[19:]
								red := color.New(color.FgRed).SprintFunc()
								//color.Tag(timePre).Println(msg.Text)
								fmt.Println(red(timePre), data)
							}
						}
						return nil
					},
			},
		},
	}

	err := app.Run(os.Args)
	if err != nil {
		log.Fatal(err)
	}
	//fmt.Println("进程出去")
}

func showList(){
	res,err := httpPost("list", map[string]interface{}{})
	if err != nil {
		fmt.Println("查询列表失败:", err.Error())
		return
	}
	if res.Data == nil{
		return
	}
	listProcess := res.Data.([]interface{})
	list := make([][]string, 0)
	for k,val := range listProcess{
		item := val.(map[string]interface{})
		list = append(list, []string{
			strconv.Itoa(k),item["name"].(string), fmt.Sprintf("%.0f", item["pid"].(float64)), fmt.Sprintf("%.0f",item["status"].(float64)), item["cpu"].(string), item["memory"].(string), time.Unix(int64(item["date"].(float64)),  0).Format("2006-01-02 15:04:05"),
		})
	}
	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"Id", "Name", "Pid", "Status", "Cpu","Memory", "Date"})
	hColor := tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiCyanColor}
	table.SetHeaderColor(hColor, hColor, hColor, hColor, hColor,hColor, hColor)
	for _, row := range list {
		switch row[3] {
		case "0":
			row[3] = "正常"
			table.Rich(row, []tablewriter.Colors{{tablewriter.FgHiYellowColor}, {tablewriter.FgHiWhiteColor}, {tablewriter.FgHiWhiteColor}, {tablewriter.Bold, tablewriter.FgHiGreenColor}, {}, {}, {}, {tablewriter.FgWhiteColor}})
		case "1":
			row[3] = "停止"
			table.Rich(row, []tablewriter.Colors{{tablewriter.FgHiYellowColor}, {tablewriter.FgHiWhiteColor}, {tablewriter.FgHiWhiteColor}, {tablewriter.Bold, tablewriter.FgYellowColor}, {}, {}, {}, {tablewriter.FgWhiteColor}})
		}
	}
	table.SetColumnAlignment([]int{tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER})
	table.Render()
}


func showInfo(key string){
	res,err := httpPost("info", map[string]interface{}{"key":key})
	if err != nil{
		fmt.Println("查询列表失败:", err.Error())
		return
	}
	if res.Data == nil{
		return
	}
	pr := res.Data.(map[string]interface{})
	tbData := make([][]string, 0)
	var statusKey int
	for k,item:=range pr {
		if k == "status" || k=="pid"{
			tbData = append(tbData,[]string{
				k, fmt.Sprintf("%.0f", item.(float64)),
			})
			if k == "status"{
				statusKey = len(tbData) -1
			}
		}else if k == "date"{
			dateFormat := time.Unix(int64(item.(float64)),  0).Format("2006-01-02 15:04:05")
			tbData = append(tbData,[]string{
				k,dateFormat,
			})
		}else{
			tbData = append(tbData,[]string{
				k, item.(string),
			})
		}
	}
	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"--", "Desc"})
	hColor := tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiCyanColor}
	table.SetHeaderColor(hColor, hColor)
	table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
	for index, row := range tbData {
		if index == statusKey {
			switch row[1] {
			case "0":
				row[1] = "正常"
				table.Rich(row, []tablewriter.Colors{{}, {tablewriter.Bold, tablewriter.FgHiGreenColor}})
				break
			case "1":
				row[1] = "停止"
				table.Rich(row, []tablewriter.Colors{{}, {tablewriter.Bold, tablewriter.FgHiYellowColor}})
				break
			default:
				table.Append(row)
			}
		} else {
			table.Append(row)
		}
	}

	//table.SetRowLine(true)
	table.SetColumnAlignment([]int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT})
	table.Render()
}